
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>智能防重叠气泡图</title>
    <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
    <style>
        body {
            margin: 0;
            padding: 20px;
            background: #f5f7fa;
            font-family: 'Arial', sans-serif;
        }
        #chart-container {
            width: 100%;
            height: 700px;
            background: white;
            border-radius: 12px;
            box-shadow: 0 4px 20px rgba(0,0,0,0.08);
            padding: 20px;
            box-sizing: border-box;
        }
        .title {
            text-align: center;
            color: #333;
            margin-bottom: 30px;
            font-size: 24px;
            font-weight: 600;
        }
    </style>
</head>
<body>
    <div class="title">智能防重叠气泡图</div>
    <div id="chart-container"></div>

    <script>
        // 初始化图表
        const chartDom = document.getElementById('chart-container');
        const myChart = echarts.init(chartDom);
        
        // 生成模拟数据
        function generateData() {
            const data = [];
            const categories = ['科技', '金融', '医疗', '教育', '零售', '制造'];
            
            for (let i = 0; i < 30; i++) {
                const category = categories[Math.floor(Math.random() * categories.length)];
                data.push({
                    name: `${category}公司${i+1}`,
                    value: [
                        Math.round(Math.random() * 100), // x轴
                        Math.round(Math.random() * 100), // y轴
                        Math.round(Math.random() * 50 + 10), // 气泡大小
                    ],
                    category: category,
                    itemStyle: {
                        color: getCategoryColor(category)
                    }
                });
            }
            return data;
        }
        
        // 获取分类颜色
        function getCategoryColor(category) {
            const colors = {
                '科技': '#5470c6',
                '金融': '#91cc75',
                '医疗': '#fac858',
                '教育': '#ee6666',
                '零售': '#73c0de',
                '制造': '#3ba272'
            };
            return colors[category] || '#999';
        }
        
        // 防重叠标签布局算法
        function adjustLabels(seriesData) {
            const labelPositions = [];
            const adjustedData = JSON.parse(JSON.stringify(seriesData));
            
            // 按气泡大小降序排序，优先处理大气泡
            adjustedData.sort((a, b) => b.value[2] - a.value[2]);
            
            for (let i = 0; i < adjustedData.length; i++) {
                const item = adjustedData[i];
                const radius = item.value[2] / 2;
                const x = item.value[0];
                const y = item.value[1];
                
                // 默认标签位置（上方）
                let labelX = x;
                let labelY = y - radius - 10;
                let position = 'top';
                
                // 检查是否与已有标签重叠
                let overlap = checkOverlap(labelX, labelY, item.name.length * 8, 20, labelPositions);
                
                // 如果重叠，尝试其他位置
                if (overlap) {
                    const positions = [
                        { x: x, y: y + radius + 15, pos: 'bottom' },  // 下方
                        { x: x - (item.name.length * 4 + 10), y: y, pos: 'left' },  // 左侧
                        { x: x + (item.name.length * 4 + 10), y: y, pos: 'right' }   // 右侧
                    ];
                    
                    for (let j = 0; j < positions.length; j++) {
                        const pos = positions[j];
                        if (!checkOverlap(pos.x, pos.y, item.name.length * 8, 20, labelPositions)) {
                            labelX = pos.x;
                            labelY = pos.y;
                            position = pos.pos;
                            overlap = false;
                            break;
                        }
                    }
                }
                
                // 如果仍然重叠，尝试偏移位置
                if (overlap) {
                    let offset = 10;
                    let found = false;
                    
                    // 尝试在圆周上寻找合适位置
                    for (let angle = 0; angle < 360; angle += 30) {
                        const rad = angle * Math.PI / 180;
                        const testX = x + Math.cos(rad) * (radius + 15);
                        const testY = y + Math.sin(rad) * (radius + 15);
                        
                        if (!checkOverlap(testX, testY, item.name.length * 8, 20, labelPositions)) {
                            labelX = testX;
                            labelY = testY;
                            position = 'outside';
                            found = true;
                            break;
                        }
                    }
                    
                    if (!found) {
                        // 如果还是找不到位置，缩小字体或不显示标签
                        item.label = { show: false };
                        continue;
                    }
                }
                
                // 记录已占用的标签位置
                labelPositions.push({
                    x: labelX,
                    y: labelY,
                    width: item.name.length * 8,
                    height: 20
                });
                
                // 设置标签配置
                item.label = {
                    show: true,
                    position: position,
                    formatter: '{b}',
                    color: '#333',
                    fontSize: 12,
                    fontWeight: 'normal',
                    padding: [2, 5],
                    backgroundColor: 'rgba(255,255,255,0.9)',
                    borderColor: '#ddd',
                    borderWidth: 1,
                    borderRadius: 4,
                    shadowBlur: 4,
                    shadowColor: 'rgba(0,0,0,0.1)',
                    shadowOffsetY: 2
                };
            }
            
            return adjustedData;
        }
        
        // 检查矩形区域是否重叠
        function checkOverlap(x, y, width, height, positions) {
            for (const pos of positions) {
                if (!(x + width < pos.x || 
                      x > pos.x + pos.width || 
                      y + height < pos.y || 
                      y > pos.y + pos.height)) {
                    return true;
                }
            }
            return false;
        }
        
        // 准备图表配置
        const rawData = generateData();
        const adjustedData = adjustLabels(rawData);
        
        const option = {
            title: {
                text: '公司市场表现分析',
                subtext: '气泡大小代表市场份额',
                left: 'center',
                textStyle: {
                    fontSize: 18,
                    color: '#333'
                }
            },
            tooltip: {
                trigger: 'item',
                formatter: params => {
                    return `
                        <div style="font-weight:bold">${params.name}</div>
                        <div>市场份额: ${params.value[2]}%</div>
                        <div>X指标: ${params.value[0]}</div>
                        <div>Y指标: ${params.value[1]}</div>
                    `;
                }
            },
            legend: {
                data: ['科技', '金融', '医疗', '教育', '零售', '制造'],
                bottom: 10,
                itemGap: 20,
                textStyle: {
                    fontSize: 12
                }
            },
            xAxis: {
                name: '市场增长率(%)',
                nameLocation: 'middle',
                nameGap: 30,
                type: 'value',
                splitLine: {
                    lineStyle: {
                        type: 'dashed'
                    }
                },
                axisLine: {
                    lineStyle: {
                        color: '#999'
                    }
                }
            },
            yAxis: {
                name: '客户满意度(%)',
                nameLocation: 'middle',
                nameGap: 30,
                type: 'value',
                splitLine: {
                    lineStyle: {
                        type: 'dashed'
                    }
                },
                axisLine: {
                    lineStyle: {
                        color: '#999'
                    }
                }
            },
            series: [{
                name: '公司数据',
                type: 'scatter',
                symbolSize: function (data) {
                    return data[2];
                },
                data: adjustedData,
                emphasis: {
                    label: {
                        show: true,
                        fontSize: 14,
                        fontWeight: 'bold'
                    },
                    itemStyle: {
                        shadowBlur: 10,
                        shadowColor: 'rgba(0, 0, 0, 0.5)'
                    }
                },
                animationDelay: function (idx) {
                    return idx * 50;
                }
            }],
            animationEasing: 'elasticOut',
            animationDelayUpdate: function (idx) {
                return idx * 5;
            }
        };
        
        // 应用配置
        myChart.setOption(option);
        
        // 响应窗口大小变化
        window.addEventListener('resize', function() {
            myChart.resize();
        });
    </script>
</body>
</html>
